//////////////////////////////////////////////////////////////////////////
// q3dModel - model in Quebra3D
// By Antonio Miranda - June 2014
//////////////////////////////////////////////////////////////////////////

#include <math.h>
#include <map>

#include "q3dModel.h"
#include "q3dSegment.h"
#include "q3dVertex.h"
#include "q3dSurface.h"

#include "auxiliary/decompBoundary.h"

using namespace std;

//
//////////////////////////////////////////////////////////////////////////
q3dModel::q3dModel ()
{
  m_mesh3d = NULL;
 
}

//
//////////////////////////////////////////////////////////////////////////
q3dModel::~q3dModel(void)
{
}

//
//////////////////////////////////////////////////////////////////////////
bool q3dModel::set3DMesh( tetraTopology *mesh3D, bool build_structure /*= false*/ )
{
  if (mesh3D == NULL)
    return false;

  // set mesh3d
  m_mesh3d = mesh3D;

  if (!build_structure)
    return true;

  // build structures
  m_boun3D.clear();
  m_boun3D.process(m_mesh3d);

  // create entities
  createEntFromMesh3D ();



  return true;
}

//
//////////////////////////////////////////////////////////////////////////
void q3dModel::createEntFromMesh3D()
{
  
  double coord[3];
  
  m_vertex.clear();
  m_segment.clear();
  m_surface.clear();

  cTopology *bound = m_boun3D.m_boundary;

  // decompose boundary
  decompBoundary decompose;
  decompose.setBoudary(bound);
  decompose.process();

  // add vertex
  int n_vxt = decompose.m_outVertex.size();
  if (n_vxt > 0)
  {

    m_vertex.resize(n_vxt);
    for (int i = 0; i < n_vxt; ++i)
    {
      // create
      q3dVertex *new_vtx = new q3dVertex;
      m_vertex[i] = new_vtx;

      // set id
      new_vtx->id_boundary = decompose.m_outVertex[i];
      
      // coordinate
      bound->GetCoordNode(new_vtx->id_boundary, coord);
      new_vtx->m_pts.setXYZ(coord[0], coord[1], coord[2]);
    }
  }

  // add curves
  int n_sgm = decompose.m_outCurves.size();
  if (n_sgm > 0)
  {
    m_segment.resize(n_sgm);

    for (int i = 0; i < n_sgm; ++i)
    {
      // create
      q3dSegment *new_sgm = new q3dSegment;
      m_segment[i] = new_sgm;

      // create curve
      vector <Point3D> pts;
      size_t n_pts = decompose.m_outCurves[i].idpts.size();
      pts.resize(n_pts);
      for (size_t k = 0; k < n_pts; ++k)
      {
        bound->GetCoordNode(decompose.m_outCurves[i].idpts[k], coord);
        pts[k].setXYZ(coord[0], coord[1], coord[2]);
      }
      new_sgm->createCurve (pts);
    }
  }

  // create surfaces
  int n_surf = decompose.m_outMeshes.size();
  if (n_surf > 0)
  {
    m_surface.resize (n_surf);
    for (int i = 0; i < n_surf; ++i)
    {
      // create
      q3dSurface *new_surf = new q3dSurface;
      m_surface[i] = new_surf;

      new_surf->m_support = decompose.m_outMeshes[i];
    }
  }

  // create points
  createAdjPointers (&decompose);

}

//////////////////////////////////////////////////////////////////////////
void q3dModel::createAdjPointers( decompBoundary *decomp )
{
  // create a map to fast search id
  map <int, int> node_vertex;
  map <int, int>::iterator itr;
  for (int i = 0; i < decomp->m_outVertex.size(); ++i)
  {
    node_vertex.insert(make_pair (decomp->m_outVertex[i], i));
  
    // clear adjacent segments on vertex
    m_vertex[i]->m_adjSegm.clear();
    m_vertex[i]->m_adjSurf.clear();
  }

  // update segments and vertex
  for (int i = 0; i < m_segment.size(); ++i)
  {
    // update vertex on segment
    itr = node_vertex.find ( decomp->m_outCurves[i].adjVertices[0]);
    m_segment[i]->m_adjVextex[0] = m_vertex[(*itr).second];

    m_vertex[(*itr).second]->m_adjSegm.push_back(m_segment[i]);

    itr = node_vertex.find ( decomp->m_outCurves[i].adjVertices[1]);
    m_segment[i]->m_adjVextex[1] = m_vertex[(*itr).second];

    m_vertex[(*itr).second]->m_adjSegm.push_back(m_segment[i]);
  }

  // update curves on surfaces 
  // clear all segments
  for (int i = 0; i < m_surface.size(); ++i)
  {
    m_surface[i]->adjSegm.clear();
    m_surface[i]->orientation.clear();

    // set pointer for curves
    int id_cv, n_adj_cvs = decomp->m_adjCurveToMeshes[i].size();
    for (int k = 0; k < n_adj_cvs; ++k)
    {
      id_cv = decomp->m_adjCurveToMeshes[i][k]; 
      m_surface[i]->adjSegm.push_back(m_segment[id_cv]);

      // copy orientation
      m_surface[i]->orientation.push_back(decomp->m_orientCurveMeshes[i][k]);
    }
  }


  // update surfaces on curve 
  for (int i = 0; i < m_surface.size(); ++i)
  {
    int n_adj_cvs = decomp->m_adjCurveToMeshes[i].size();

    // init pointer = NULL
    for (int k = 0; k < n_adj_cvs; ++k)
    {
      m_surface[i]->adjSegm[k]->m_adjSurf[0] = NULL;
      m_surface[i]->adjSegm[k]->m_adjSurf[1] = NULL;
    }
  }
  // update surfaces on curve 
  for (int i = 0; i < m_surface.size(); ++i)
  {
    int n_adj_cvs = decomp->m_adjCurveToMeshes[i].size();

    // set pointer for surface
    for (int k = 0; k < n_adj_cvs; ++k)
    {
      if (m_surface[i]->adjSegm[k]->m_adjSurf[0] == NULL)
        m_surface[i]->adjSegm[k]->m_adjSurf[0] = m_surface[i];
      else
        m_surface[i]->adjSegm[k]->m_adjSurf[1] = m_surface[i];
    }
  }

}
